/**
 * @copyright @2015-2016 Abel-Studio.All Rights Reserved
 */
package com.yoooya.base.utils.qrcode;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.StringUtils;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

/**
 * 
 * @author Sun Yijing<br />
 *         Copyright © Xiamen Yoooya NetWorks Co.,Ltd.
 * @since yoooya software platform v1.0.0
 * @version 1.0.0 <br />
 *          <table border="1" cellpadding="3" cellspacing="0">
 *          <tr>
 *          <th align="left" colspan="3"><b>修改历史</b></th>
 *          </tr>
 *          <tr>
 *          <td>修改日期</td>
 *          <td>修改人</td>
 *          <td>修改描述</td>
 *          </tr>
 *          <tr>
 *          <td>2018-6-20</td>
 *          <td>Sun Yijing</td>
 *          <td>新建</td>
 *          </tr>
 *          </table>
 */
public class QRCodeUtil {
	private static int width = 500; // 二维码宽度
	private static int height = 500; // 二维码高度
	private static int onColor = 0xFF000000; // 前景色
	private static int offColor = 0xFFFFFFFF; // 背景色
	private static int margin = 1; // 白边大小，取值范围0~4
	private static ErrorCorrectionLevel level = ErrorCorrectionLevel.L; // 二维码容错率

	/**
	 * 生成二维码
	 * 
	 * @param params
	 *            QRCodeParams属性：txt、fileName、filePath不得为空；
	 * @throws QRParamsException
	 */
	public static ByteArrayOutputStream generateQRImage(QRCodeParams params)
			throws QRParamsException {
		if (params == null || params.getTxt() == null) {
			throw new QRParamsException("参数错误");
		}
		try {
			initData(params);
			String txt = params.getTxt();
			if (StringUtils.isNoneBlank(params.getLogoPath())) {
				return generateQRImage(txt, params.getLogoPath());
			} else {
				return generateQRImage(txt);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 生成二维码
	 * 
	 * @param txt
	 *            //二维码内容
	 */
	public static ByteArrayOutputStream generateQRImage(String txt) {
		Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
		// 指定纠错等级
		hints.put(EncodeHintType.ERROR_CORRECTION, level);
		// 指定编码格式
		hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
		hints.put(EncodeHintType.MARGIN, margin);
		try {
			BitMatrix bitMatrix = new MultiFormatWriter().encode(txt,
					BarcodeFormat.QR_CODE, width, height, hints);
			BufferedImage image = toBufferedImage(bitMatrix);
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			try {
				ImageIO.write(image, "PNG", out);
				return out;
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 生成带logo的二维码图片
	 * 
	 * @param txt
	 *            //二维码内容
	 * @param logoPath
	 *            //logo绝对物理路径
	 * @throws Exception
	 */
	public static ByteArrayOutputStream generateQRImage(String txt,
			String logoPath) throws Exception {
		Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
		// 指定纠错等级,纠错级别（L 7%、M 15%、Q 25%、H 30%）
		hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
		hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
		hints.put(EncodeHintType.ERROR_CORRECTION, level);
		hints.put(EncodeHintType.MARGIN, margin);
		BitMatrix bitMatrix = new MultiFormatWriter().encode(txt,
				BarcodeFormat.QR_CODE, width, height, hints);
		return writeToFile(bitMatrix, logoPath);
	}

	/**
	 * 
	 * @param matrix
	 *            二维码矩阵相关
	 * @param format
	 *            二维码图片格式
	 * @param file
	 *            二维码图片文件
	 * @param logoPath
	 *            logo路径
	 * @throws IOException
	 */
	public static void writeToFile(BitMatrix matrix, String format, File file,
			String logoPath) throws IOException {
		BufferedImage image = toBufferedImage(matrix);
		Graphics2D gs = image.createGraphics();

		int ratioWidth = image.getWidth() * 2 / 10;
		int ratioHeight = image.getHeight() * 2 / 10;
		// 载入logo
		Image img = ImageIO.read(new File(logoPath));
		int logoWidth = img.getWidth(null) > ratioWidth ? ratioWidth : img
				.getWidth(null);
		int logoHeight = img.getHeight(null) > ratioHeight ? ratioHeight : img
				.getHeight(null);

		int x = (image.getWidth() - logoWidth) / 2;
		int y = (image.getHeight() - logoHeight) / 2;

		gs.drawImage(img, x, y, logoWidth, logoHeight, null);
		gs.setColor(Color.black);
		gs.setBackground(Color.WHITE);
		gs.dispose();
		img.flush();
		if (!ImageIO.write(image, format, file)) {
			throw new IOException("Could not write an image of format "
					+ format + " to " + file);
		}
	}

	public static ByteArrayOutputStream writeToFile(BitMatrix matrix,
			String logoPath) throws IOException {
		BufferedImage image = toBufferedImage(matrix);
		// 设置logo图标
		QRLogoConfig logoConfig = new QRLogoConfig();
		image = logoConfig.LogoMatrix(image, logoPath);

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			ImageIO.write(image, "PNG", out);
			return out;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	public static BufferedImage toBufferedImage(BitMatrix matrix) {
		int width = matrix.getWidth();
		int height = matrix.getHeight();
		BufferedImage image = new BufferedImage(width, height,
				BufferedImage.TYPE_INT_RGB);

		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				image.setRGB(x, y, matrix.get(x, y) ? onColor : offColor);
			}
		}
		return image;
	}

	public static BitMatrix deleteWhite(BitMatrix matrix) {
		int[] rec = matrix.getEnclosingRectangle();
		int resWidth = rec[2] + 1;
		int resHeight = rec[3] + 1;

		BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
		resMatrix.clear();
		for (int i = 0; i < resWidth; i++) {
			for (int j = 0; j < resHeight; j++) {
				if (matrix.get(i + rec[0], j + rec[1]))
					resMatrix.set(i, j);
			}
		}
		return resMatrix;
	}

	private static void initData(QRCodeParams params) {
		if (params.getWidth() != null) {
			width = params.getWidth();
		}
		if (params.getHeight() != null) {
			height = params.getHeight();
		}
		if (params.getOnColor() != null) {
			onColor = params.getOnColor();
		}
		if (params.getOffColor() != null) {
			offColor = params.getOffColor();
		}
		if (params.getLevel() != null) {
			level = params.getLevel();
		}
	}
}
